package cn.ivicar.app.controller.guice;

import java.io.IOException;
import java.util.Map;
import java.util.Properties;

import org.mybatis.guice.XMLMyBatisReplicableModule;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.inject.AbstractModule;
import com.google.inject.Key;
import com.google.inject.PrivateModule;
import com.google.inject.Scopes;
import com.google.inject.name.Names;

import cn.com.carsmart.util.ExceptionUtil;
import cn.com.carsmart.util.configmanagement.ConfigUtil;
import cn.com.carsmart.ws.ibatis.mapper.BaseMapper;

public class AppMybatisDataBaseModule extends PrivateModule {

    private static transient final Logger LOGGER = LoggerFactory.getLogger(AppMybatisDataBaseModule.class);

    private String moduleName;
    private Map<String, String> bindings;

    public AppMybatisDataBaseModule(String moduleName, Map<String, String> bindings) {
        this.moduleName = moduleName;
        this.bindings = bindings;
    }

    @Override
    protected void configure() {
        AbstractModule module = createMybatisModule();
        install(module);

        for (Map.Entry<String, String> item : this.bindings.entrySet()) {
            try {
                bindDatabase("cn.com.carsmart.ws", item.getValue(), item.getKey());
            } catch (ClassNotFoundException e) {
                LOGGER.error(e.getMessage());
            }
        }
    }

    private AbstractModule createMybatisModule() {
        AbstractModule module = null;

        final Properties props = loadProperties();

        module = new XMLMyBatisReplicableModule() {
            @Override
            protected void initialize() {
                setClassPathResource(AppMybatisDataBaseModule.this.moduleName + "-mybatis-config.xml");
                setEnvironmentId("production");
                this.addProperties(props);
                Names.bindProperties(this.binder(), props);
            }
        };

        return module;
    }

    private Properties loadProperties() {
        Properties props = null;
        Properties rsp = new Properties();
        try {
            props = ConfigUtil.getConfigReader().getResourceAsProperties(this.moduleName + ".properties");

            String database = (String) props.get("database");
            if (database == null) {
                props.put("database", "");
                return props;
            } else {
                props.put("database", database + "/");
                rsp.put("database", database + "/");
                for (Object key : props.keySet()) {
                    if (key instanceof String) {
                        if (((String) key).startsWith(database)) {
                            String tempkey = ((String) key).substring(database.length() + 1);
                            rsp.put(tempkey, props.get(key));
                        }
                    }
                }
            }
        } catch (IOException e) {
            LOGGER.error(ExceptionUtil.getLogMessage(e));
        }

        return rsp;
    }

    @SuppressWarnings({ "rawtypes", "unchecked" })
    private void bindDatabase(String databaseNamespace, String subDomain, String objectName)
            throws ClassNotFoundException {
        if (!objectName.endsWith("Db")) {
            String mapperName = databaseNamespace + ".ibatis.mapper." + subDomain + objectName + "Mapper";
            String managerName = databaseNamespace + ".service." + subDomain + objectName + "Manager";
            String managerImplName = databaseNamespace + ".service.impl." + subDomain + objectName + "ManagerImpl";

            Class mapperClass = Class.forName(mapperName, true, getClass().getClassLoader());
            Class managerClass = Class.forName(managerName, true, getClass().getClassLoader());
            Class managerImplClass = Class.forName(managerImplName, true, getClass().getClassLoader());

            bind(managerClass).to(managerImplClass).in(Scopes.NO_SCOPE);
            bind(managerClass).annotatedWith(Names.named(objectName + "Manager")).to(managerImplClass)
                    .in(Scopes.NO_SCOPE);
            bind(BaseMapper.class).annotatedWith(Names.named(objectName + "Mapper")).to(mapperClass)
                    .in(Scopes.NO_SCOPE);
            expose(managerClass);
            expose(Key.get(managerClass, Names.named(objectName + "Manager")));
            expose(Key.get(BaseMapper.class, Names.named(objectName + "Mapper")));
        } else {
            String managerImplName = databaseNamespace + ".service.impl." + subDomain + objectName + "ManagerImpl";
            Class managerImplClass = Class.forName(managerImplName, true, getClass().getClassLoader());
            bind(managerImplClass);
            expose(managerImplClass);
        }
    }

}
